package security;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import models.Account;
import models.TokenAction;
import models.User;
import play.Application;
import play.data.Form;
import play.data.validation.Constraints.MinLength;
import play.data.validation.Constraints.Required;
import play.i18n.Messages;
import play.mvc.Call;
import play.mvc.Http.Context;

import com.avaje.ebean.validation.Email;
import com.feth.play.module.mail.Mailer.Mail.Body;
import com.feth.play.module.pa.PlayAuthenticate;
import com.feth.play.module.pa.providers.password.UsernamePasswordAuthProvider;
import com.feth.play.module.pa.providers.password.UsernamePasswordAuthUser;

import controllers.routes;

public class ConsoleUsernamePasswordAuthProvider extends UsernamePasswordAuthProvider<String, ConsoleLoginUsernamePasswordAuthUser, ConsoleUsernamePasswordAuthUser, ConsoleUsernamePasswordAuthProvider.ConsoleLogin, ConsoleUsernamePasswordAuthProvider.ConsoleSignup> {
	
	public static final String PROVIDER_KEY = UsernamePasswordAuthProvider.PROVIDER_KEY;
	
	private static final String SETTING_KEY_VERIFICATION_LINK_SECURE = SETTING_KEY_MAIL + ".verificationLink.secure";
	private static final String SETTING_KEY_PASSWORD_RESET_LINK_SECURE = SETTING_KEY_MAIL + ".passwordResetLink.secure";
	private static final String SETTING_KEY_LINK_LOGIN_AFTER_PASSWORD_RESET = "loginAfterPasswordReset";
	
	public static final String ERROR_LOGIN_USER_NOT_FOUND = "auth-invalid-credentials";
	
	// private static final String EMAIL_TEMPLATE_FALLBACK_LANGUAGE = "en";
	
	public static final Form<ConsoleSignup> FORM_SIGNUP = Form.form(ConsoleSignup.class);
	public static final Form<ConsoleLogin> FORM_LOGIN = Form.form(ConsoleLogin.class);
	
	public ConsoleUsernamePasswordAuthProvider(Application app) {
		super(app);
	}
	
	protected Form<ConsoleSignup> getSignupForm() {
		return FORM_SIGNUP;
	}
	
	protected Form<ConsoleLogin> getLoginForm() {
		return FORM_LOGIN;
	}
	
	protected List<String> neededSettingKeys() {
		final List<String> needed = new ArrayList<String>(super.neededSettingKeys());
		needed.add(SETTING_KEY_VERIFICATION_LINK_SECURE);
		needed.add(SETTING_KEY_PASSWORD_RESET_LINK_SECURE);
		needed.add(SETTING_KEY_LINK_LOGIN_AFTER_PASSWORD_RESET);
		return needed;
	}
	
	@Override
	protected ConsoleLoginUsernamePasswordAuthUser buildLoginAuthUser(ConsoleLogin login, Context context) {
		return new ConsoleLoginUsernamePasswordAuthUser(login.getPassword(), login.getEmail());
	}
	
	@Override
	protected ConsoleUsernamePasswordAuthUser buildSignupAuthUser(ConsoleSignup signup, Context context) {
		return new ConsoleUsernamePasswordAuthUser(signup);
	}
	
	@Override
	protected String generateVerificationRecord(ConsoleUsernamePasswordAuthUser authUser) {
		return generateVerificationRecord(User.findByAuthUserIdentity(authUser));
	}
	
	protected String generateVerificationRecord(User user) {
		String token = generateToken();
		TokenAction.create(user, TokenAction.Type.EMAIL_VERIFICATION, token);
		return token;
	}
	
	protected String generateToken() {
		return UUID.randomUUID().toString();
	}
	
	@Override
	protected String onLoginUserNotFound(Context context) {
		context.flash().put(ERROR_LOGIN_USER_NOT_FOUND, ERROR_LOGIN_USER_NOT_FOUND);
		return super.onLoginUserNotFound(context);
	}
	
	@Override
	protected com.feth.play.module.pa.providers.password.UsernamePasswordAuthProvider.LoginResult loginUser(ConsoleLoginUsernamePasswordAuthUser authUser) {
		User user = User.findByUsernamePasswordIdentity(authUser);
		if (user != null) {
			if (!user.getEmailValidated()) {
				return LoginResult.USER_UNVERIFIED;
			} else {
				for (final Account account : user.getAccounts()) {
					if (getKey().equals(account.getProviderKey())) {
						if (authUser.checkPassword(account.getProviderUserId(), authUser.getPassword())) {
							return LoginResult.USER_LOGGED_IN;
						} else {
							return LoginResult.WRONG_PASSWORD;
						}
					}
				}
				return LoginResult.WRONG_PASSWORD;
			}
		} else {
			return LoginResult.NOT_FOUND;
		}
	}
	
	@Override
	protected com.feth.play.module.pa.providers.password.UsernamePasswordAuthProvider.SignupResult signupUser(ConsoleUsernamePasswordAuthUser authUser) {
		User user = User.findByUsernamePasswordIdentity(authUser);
		if (user != null) {
			if (user.getEmailValidated()) {
				return SignupResult.USER_EXISTS;
			} else {
				return SignupResult.USER_EXISTS_UNVERIFIED;
			}
		}
		User.create(authUser);
		return SignupResult.USER_CREATED_UNVERIFIED;
	}
	
	@Override
	protected ConsoleLoginUsernamePasswordAuthUser transformAuthUser(ConsoleUsernamePasswordAuthUser authUser, Context context) {
		return new ConsoleLoginUsernamePasswordAuthUser(authUser.getEmail());
	}
	
	@Override
	protected Call userExists(UsernamePasswordAuthUser authUser) {
		return routes.Application.index();
	}
	
	@Override
	protected Call userUnverified(UsernamePasswordAuthUser authUser) {
		return routes.Application.index();
	}
	
	@Override
	protected Body getVerifyEmailMailingBody(String token, ConsoleUsernamePasswordAuthUser authUser, Context context) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	protected String getVerifyEmailMailingSubject(ConsoleUsernamePasswordAuthUser authUser, Context context) {
		// TODO Auto-generated method stub
		return null;
	}
	
	public static ConsoleUsernamePasswordAuthProvider getProvider() {
		return (ConsoleUsernamePasswordAuthProvider) PlayAuthenticate.getProvider(UsernamePasswordAuthProvider.PROVIDER_KEY);
	}
	
	public static class ConsoleIdentity {
		
		@Required
		@Email
		public String email;
		
		public ConsoleIdentity() {
		}
		
		public ConsoleIdentity(final String email) {
			this.email = email;
		}
	}
	
	public static class ConsoleLogin extends ConsoleIdentity implements UsernamePasswordAuthProvider.UsernamePassword {
		
		@Required
		@MinLength(6)
		public String password;
		
		@Override
		public String getEmail() {
			return email;
		}
		
		@Override
		public String getPassword() {
			return password;
		}
		
	}
	
	public static class ConsoleSignup extends ConsoleLogin {
		
		@Required
		@MinLength(6)
		public String repeatPassword;
		
		@Required
		public String firstName;
		
		@Required
		public String lastName;
		
		public String validate() {
			if (password == null || !password.equals(repeatPassword)) {
				return Messages.get("playauthenticate.password.signup.error.passwords_not_same");
			}
			return null;
		}
		
	}
	
}
